/*
 * Copyright (c) 2007 Compl Yue Still, All Rights Reserved.
 */

package hbi.java.util;

import java.util.Date;

public class DataCompat
{

    public static boolean isTypeCompatible(Class<?> expectedType,
        Class<?> actualType)
    {
        if (expectedType == null || expectedType == Object.class) return true;

        if (expectedType.isAssignableFrom(actualType)) return true;

        if (expectedType.isArray())
        {
            final Class<?> expectedComponentType = expectedType
                .getComponentType();

            if (expectedComponentType.isArray())
                throw new RuntimeException(
                    "Cast of multi-dimentional array not implemented yet!");

            if (expectedComponentType == Byte.TYPE) // byte[]
            {
                if (actualType == String.class) // String -> byte[]
                    return true;
            }
            else if (actualType.isArray()) // array -> array
            {
                Class<?> componentType = actualType.getComponentType();

                // TODO: complete array cast logics below
                if (expectedComponentType == Boolean.TYPE)
                {
                    if (componentType == Integer.TYPE) // int[] -> boolean[]
                        return true;
                }
                else if (expectedComponentType == Integer.TYPE)
                {
                }
                else if (expectedComponentType == Long.TYPE)
                {
                }
                else if (expectedComponentType == Double.TYPE)
                {
                    if (componentType == Integer.TYPE) // int[] -> double[]
                        return true;
                }
                else if (expectedComponentType == String.class)
                {
                }
                else if (expectedComponentType == Date.class)
                {
                }
            }
        }
        else if (expectedType == Boolean.TYPE)
        {
            if (actualType == Integer.TYPE) // int -> boolean
                return true;
            if (actualType == Long.TYPE) // long -> boolean
                return true;
            if (actualType == Double.TYPE) // double -> boolean
                return true;
        }
        else if (expectedType == Integer.TYPE)
        {
            if (actualType == Boolean.TYPE) // boolean -> int
                return true;
            if (actualType == Long.TYPE) // long -> int
                return true;
            if (actualType == Double.TYPE) // double -> int
                return true;
        }
        else if (expectedType == Long.TYPE)
        {
            if (actualType == Boolean.TYPE) // boolean -> long
                return true;
            if (actualType == Integer.TYPE) // int -> long
                return true;
            if (actualType == Double.TYPE) // double -> long
                return true;
            if (actualType == Date.class) // date -> long
                return true;
        }
        else if (expectedType == Double.TYPE)
        {
            if (actualType == Boolean.TYPE) // boolean -> double
                return true;
            if (actualType == Integer.TYPE) // int -> double
                return true;
            if (actualType == Long.TYPE) // long -> double
                return true;
        }
        else if (expectedType == String.class)
        {
            return true;
        }
        else if (expectedType == Date.class)
        {
            if (actualType == Long.TYPE) // long -> date
                return true;
            if (actualType == Integer.TYPE) // int -> date
                return true;
            if (actualType == Double.TYPE) // double -> date
                return true;
        }

        return false;
    }

    public static Object castValue(Class<?> expectedType, Object value)
    {
        if (expectedType == null || expectedType == Object.class) return value;

        assert value != null : "null shouldn't get to here";

        if (expectedType.isInstance(value)) return value;

        if (expectedType.isArray())
        {
            final Class<?> expectedComponentType = expectedType
                .getComponentType();

            if (expectedComponentType.isArray())
                throw new RuntimeException(
                    "Cast of multi-dimentional array not implemented yet!");

            if (expectedComponentType == Byte.TYPE) // byte[]
            {
                if (value instanceof String)
                    return ((String) value).getBytes(CharSetConstants.UTF8);
            }
            else if (value.getClass().isArray())
            {
                Class<?> componentType = value.getClass().getComponentType();

                // TODO: complete array cast logics below
                if (expectedComponentType == Boolean.TYPE)
                {
                    if (componentType == Integer.TYPE) // int[] -> boolean[]
                    {
                        int[] in = (int[]) value;
                        boolean[] out = new boolean[in.length];
                        for (int i = 0; i < in.length; i++)
                            out[i] = in[i] != 0;
                        return out;
                    }
                }
                else if (expectedComponentType == Integer.TYPE)
                {
                }
                else if (expectedComponentType == Long.TYPE)
                {
                }
                else if (expectedComponentType == Double.TYPE)
                {
                    if (componentType == Integer.TYPE) // int[] -> double[]
                    {
                        int[] in = (int[]) value;
                        double[] out = new double[in.length];
                        for (int i = 0; i < in.length; i++)
                            out[i] = (double) in[i];
                        return out;
                    }
                }
                else if (expectedComponentType == String.class)
                {
                }
                else if (expectedComponentType == Date.class)
                {
                }
            }
        }
        else if (expectedType == Boolean.TYPE)
        {
            if (value instanceof Boolean) // primitive wrapper
                return value;
            if (value instanceof Integer) // int -> boolean
                return ((Integer) value).intValue() != 0;
            if (value instanceof Long) // long -> boolean
                return ((Long) value).longValue() != 0L;
            if (value instanceof Double) // double -> boolean
                return ((Double) value).doubleValue() != 0.0D;
        }
        else if (expectedType == Integer.TYPE)
        {
            if (value instanceof Integer) // primitive wrapper
                return value;
            if (value instanceof Boolean) // boolean -> int
                return value == Boolean.TRUE ? 1 : 0;
            if (value instanceof Long) // long -> int
                return ((Long) value).intValue();
            if (value instanceof Double) // double -> int
                return ((Double) value).intValue();
        }
        else if (expectedType == Long.TYPE)
        {
            if (value instanceof Long) // primitive wrapper
                return value;
            if (value instanceof Boolean) // boolean -> long
                return value == Boolean.TRUE ? 1L : 0L;
            if (value instanceof Integer) // int -> long
                return ((Integer) value).longValue();
            if (value instanceof Double) // double -> long
                return ((Double) value).longValue();
            if (value instanceof Date) // date -> long
                return ((Date) value).getTime();
        }
        else if (expectedType == Double.TYPE)
        {
            if (value instanceof Double) // primitive wrapper
                return value;
            if (value instanceof Boolean) // boolean -> double
                return value == Boolean.TRUE ? 1.0D : 0.0D;
            if (value instanceof Integer) // int -> double
                return ((Integer) value).doubleValue();
            if (value instanceof Long) // long -> double
                return ((Long) value).doubleValue();
        }
        else if (expectedType == String.class)
        {
            return value.toString();
        }
        else if (expectedType == Date.class)
        {
            if (value == Boolean.FALSE) // false -> epoch
                return new Date(0L);
            if (value instanceof Long) // long -> date
                return new Date(((Long) value).longValue());
            if (value instanceof Integer) // int -> date
                return new Date(((Integer) value).longValue());
            if (value instanceof Double) // double -> date
                return new Date(((Double) value).longValue());
        }

        throw new IllegalArgumentException("Incompatible value type '"
            + value.getClass().getCanonicalName() + "', '"
            + expectedType.getCanonicalName() + "' expected.");
    }

}
